package de.slothsoft.commons.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * Handler for zip files.
 * 
 * @author Steffi
 */

public class ZipHandler {

	static byte[] buffer;
	static int length;

	public ZipHandler() {
		this(8192);
	}

	public ZipHandler(int bufferLength) {
		buffer = new byte[bufferLength];
	}

	/**
	 * Unzips an archive path to a destination path
	 */

	public void unzip(String archivePath, String destinationPath)
			throws IOException {
		File archive = new File(archivePath);
		File destDir = new File(destinationPath);

		if (!destDir.exists()) {
			destDir.mkdir();
		}

		ZipFile zipFile = new ZipFile(archive);
		Enumeration<? extends ZipEntry> entries = zipFile.entries();

		while (entries.hasMoreElements()) {
			ZipEntry entry = entries.nextElement();

			String entryFileName = entry.getName();

			File dir = buildDirectoryHierarchyFor(entryFileName, destDir);
			if (!dir.exists()) {
				dir.mkdirs();
			}

			if (!entry.isDirectory()) {
				BufferedOutputStream bos = new BufferedOutputStream(
						new FileOutputStream(new File(destDir, entryFileName)));

				BufferedInputStream bis = new BufferedInputStream(
						zipFile.getInputStream(entry));

				while ((length = bis.read(buffer)) > 0) {
					bos.write(buffer, 0, length);
				}

				bos.flush();
				bos.close();
				bis.close();
			}
		}
		zipFile.close();
	}

	private File buildDirectoryHierarchyFor(String entryName, File destDir) {
		int lastIndex = entryName.lastIndexOf('/');
		String internalPathToEntry = entryName.substring(0, lastIndex + 1);
		return new File(destDir, internalPathToEntry);
	}

	/**
	 * Zips a source path to an archive path
	 */

	public void zip(String archivePath, String sourcePath) throws IOException {
		File file = new File(sourcePath);
		zip(archivePath, sourcePath, file.getParentFile().toURI());
	}

	/**
	 * Zips a source path to an archive path
	 */

	public void zip(String archivePath, String sourcePath, URI relateTo)
			throws IOException {

		ZipOutputStream zipOutputStream = new ZipOutputStream(
				new FileOutputStream(archivePath));
		File file = new File(sourcePath);
		zipFile(file, relateTo, zipOutputStream);
		zipOutputStream.close();

	}

	private static void zipFile(File file, URI relateTo,
			ZipOutputStream zipOutputStream) throws IOException {

		if (!file.isDirectory()) {
			URI relativePath = relateTo.relativize(file.toURI());
			ZipEntry entry = new ZipEntry(relativePath.toString());
			zipOutputStream.putNextEntry(entry);

			FileInputStream fis = new FileInputStream(file);
			while ((length = fis.read(buffer)) > 0) {
				zipOutputStream.write(buffer, 0, length);
			}
			fis.close();
			zipOutputStream.closeEntry();
		} else {
			File[] children = file.listFiles();
			for (int i = 0; i < children.length; i++) {
				File child = children[i];
				zipFile(child, relateTo, zipOutputStream);
			}
		}
	}

}
